using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using HadronData;

namespace Hadron
{
    struct Particle
    {
        public ParticleSystem m_particleSystem;
        public bool m_bActive;
        public Vector2 m_vecPosition;
        public Vector2 m_vecVelocity;
        public float m_fTimeElapsed;
    }

    public class ParticleSystem
    {
        private static Random s_random = new Random();

        private int m_nMaxParticles;
        private Particle[] m_arrayParticles;
        private int m_nNextEmpty;

        private float m_fEmissionRate;
        private Vector2 m_vecEmissionVelocity;
        private float m_fEmissionVelocityVariance;

        private float m_fParticleLifetime;

        private Vector2 m_vecPosition;


        private Color[] m_arrayParticleColours;
        private float m_fColourInterval;
        private bool m_bActive;
        private float m_fEmitBuffer;

        public ParticleSystem(int p_nMaxParticles, float p_fEmissionRate, float p_fParticleLifetime)
        {
            m_nMaxParticles = p_nMaxParticles;
            m_arrayParticles = new Particle[m_nMaxParticles];
            m_fEmissionRate = p_fEmissionRate;
            m_fParticleLifetime = p_fParticleLifetime;
            m_nNextEmpty = 0;
            m_arrayParticleColours = new Color[2];
            m_arrayParticleColours[0] = Color.White;
            m_arrayParticleColours[1] = Color.Black;
            m_fColourInterval = m_fParticleLifetime;

            for (int nIndex = 0; nIndex < m_arrayParticles.Length; nIndex++)
            {
                m_arrayParticles[nIndex].m_particleSystem = this;
                m_arrayParticles[nIndex].m_bActive = false;
            }

            m_bActive = false;
            m_fEmitBuffer = 0.0f;
        }

        public void EmitBatch(int p_nParticles)
        {
            m_fEmitBuffer = p_nParticles;
        }

        public void Update(GameTime p_gameTime)
        {
            float fDeltaTime = (float)p_gameTime.ElapsedGameTime.TotalSeconds;

            // process lifetimes
            for (int nIndex = 0; nIndex < m_arrayParticles.Length; nIndex++)
            {
                if (!m_arrayParticles[nIndex].m_bActive)
                    continue;

                m_arrayParticles[nIndex].m_fTimeElapsed += fDeltaTime;
                m_arrayParticles[nIndex].m_vecPosition += m_arrayParticles[nIndex].m_vecVelocity * fDeltaTime;

                if (m_arrayParticles[nIndex].m_fTimeElapsed >= m_fParticleLifetime)
                    m_arrayParticles[nIndex].m_bActive = false;
            }

            // handle emission
            if (m_bActive)
                m_fEmitBuffer += m_fEmissionRate * fDeltaTime;

            while (m_fEmitBuffer >= 1.0f)
            {
                m_arrayParticles[m_nNextEmpty].m_vecPosition = m_vecPosition;
                m_arrayParticles[m_nNextEmpty].m_vecVelocity = m_vecEmissionVelocity;

                float fAngle = (float)s_random.NextDouble() * MathHelper.TwoPi;
                float fMagnitude = (float)s_random.NextDouble() * m_fEmissionVelocityVariance;
                m_arrayParticles[m_nNextEmpty].m_vecVelocity += new Vector2((float) Math.Cos(fAngle) * fMagnitude, (float) Math.Sin(fAngle) * fMagnitude);

                m_arrayParticles[m_nNextEmpty].m_fTimeElapsed = 0.0f;
                m_arrayParticles[m_nNextEmpty].m_bActive = true;
                m_nNextEmpty = (m_nNextEmpty + 1) % m_arrayParticles.Length;
                m_fEmitBuffer -= 1.0f;
            }
        }

        public void Draw(SpriteBatch p_spriteBatch)
        {
            Vector2 vecParticleOrigin = new Vector2(4.0f, 4.0f);

            Texture2D textureParticle = AssetManager.Textures.Particle;
            foreach (Particle particle in m_arrayParticles)
            {
                if (!particle.m_bActive)
                    continue;

                int nColourIndex1 = (int)(particle.m_fTimeElapsed / m_fColourInterval);
                int nColourIndex2 = Math.Min(nColourIndex1 + 1, m_arrayParticleColours.Length - 1);
                float fInterpolation = particle.m_fTimeElapsed % m_fColourInterval;
                Vector3 vecColour1 = m_arrayParticleColours[nColourIndex1].ToVector3();
                Vector3 vecColour2 = m_arrayParticleColours[nColourIndex2].ToVector3();
                Color color = new Color(vecColour1 * (1.0f - fInterpolation) + vecColour2 * fInterpolation);

                p_spriteBatch.Draw(textureParticle, particle.m_vecPosition,
                    null, color, 0.0f, vecParticleOrigin, 1.0f + particle.m_fTimeElapsed / m_fParticleLifetime, SpriteEffects.None, 0.0f);
            }
        }

        public Color[] ParticleColours
        {
            get { return m_arrayParticleColours; }
            set
            {
                m_arrayParticleColours = value;
                m_fColourInterval = m_fParticleLifetime / (m_arrayParticleColours.Length - 1);
            }
        }

        public Vector2 Position
        {
            get { return m_vecPosition; }
            set { m_vecPosition = value; }
        }

        public Vector2 EmissionVelocity
        {
            get { return m_vecEmissionVelocity; }
            set { m_vecEmissionVelocity = value; }
        }

        public float EmissionVelocityVariance
        {
            get { return m_fEmissionVelocityVariance; }
            set { m_fEmissionVelocityVariance = value; }
        }

        public float EmissionRate
        {
            get { return m_fEmissionRate; }
            set { m_fEmissionRate = value; }
        }

        public bool Active
        {
            get { return m_bActive; }
            set { m_bActive = value; }
        }
    }
}
